import { redis } from './redis.js';
import { logger } from './logger.js';
import { config } from '../config/env.js';
import jwt from 'jsonwebtoken';

// Token 前缀

export const TOKEN_PREFIX = 'Bearer ';
// Session 前缀
export const SESSION_PREFIX = 'session:';

// 登录限制配置
const LOGIN_ATTEMPT_PREFIX = 'login:attempt:';
const LOGIN_IP_PREFIX = 'login:ip:';
const MAX_LOGIN_ATTEMPTS = 3;
const BLACK_LIST_DURATION = 10 * 60; // 10分钟，单位：秒

export enum LoginType {
  /**
   * 密码登录
   */
  PASSWORD = 'password',
  /**
   * token登录
   */
  TOKEN = 'token',
  /**
   * 自动登录
   */
  REMEMBER = 'remember',
}

/**
 * 登录用户类型
 */
export enum LoginUserType {
  /**
   * 普通用户 sys_user
   */
  FRONT = 'user',
  /**
   * 管理员 sys_admin
   */
  ADMIN = 'admin',
}

/**
 * 登录用户信息
 */
export interface LoginUserInfo {
  user_id: number | string;
  user_name: string;
  user_type: LoginUserType;
  expired?: number;
  code?: string;
}

export class LoginUtil {
  /**
   * 获取指定key的尝试次数
   * @param prefix LOGIN_ATTEMPT_PREFIX | LOGIN_IP_PREFIX
   * @param key IP地址或用户名
   * @returns 当前尝试次数
   */
  static async getAttemptCount(prefix: string, key: string): Promise<number> {
    const attemptKey = `${prefix}${key}`;
    const attempts = await redis.get(attemptKey);
    return attempts ? parseInt(attempts) : 0;
  }

  /**
   * 记录登录尝试
   * @param prefix LOGIN_ATTEMPT_PREFIX | LOGIN_IP_PREFIX
   * @param key IP地址或用户名
   * @returns 当前尝试次数
   */
  static async recordAttempt(prefix: string, key: string): Promise<number> {
    const attemptKey = `${prefix}${key}`;
    const attempts = await redis.incr(attemptKey, BLACK_LIST_DURATION);
    return attempts;
  }

  /**
   * 重置登录计数
   * @param prefix LOGIN_ATTEMPT_PREFIX | LOGIN_IP_PREFIX
   * @param key IP地址或用户名
   */
  static async resetAttempts(prefix: string, key: string): Promise<void> {
    const attemptKey = `${prefix}${key}`;
    await redis.del(attemptKey);
  }

  /**
   * 检查是否在黑名单中
   * @param user_name 用户名
   * @param ip ip地址
   * @returns true 黑名单 false 非黑名单
   */
  static async checkBlackList(user_name: string, ip: string): Promise<boolean> {
    const userAttempt = await this.getAttemptCount(LOGIN_ATTEMPT_PREFIX, user_name);
    const ipAttempt = await this.getAttemptCount(LOGIN_IP_PREFIX, ip);
    if (userAttempt >= MAX_LOGIN_ATTEMPTS || ipAttempt >= MAX_LOGIN_ATTEMPTS) {
      logger.warn(`登录失败次数过多，用户：${user_name}，IP：${ip}`);
      return true;
    }
    return false;
  }

  /**
   * 从 Redis 获取 session
   * @param sessionId sessionId
   * @returns session
   */
  static async getSessionFromRedis(sessionId: string): Promise<unknown> {
    try {
      const data = await redis.get(`${SESSION_PREFIX}${sessionId}`);
      return data ? JSON.parse(data) : null;
    } catch (error) {
      logger.error('从 Redis 获取 session 失败', { error, sessionId });
      return null;
    }
  }

  /**
   * 存储用户信息到 redis 并返回sessionId
   * @param userInfo 用户信息
   * @returns sessionId
   */
  static async createSession(userInfo: LoginUserInfo): Promise<string> {
    const sessionId = `${userInfo.user_type}:${userInfo.user_id}:${Date.now()}`;
    await redis.set(
      `${SESSION_PREFIX}${sessionId}`,
      JSON.stringify(userInfo),
      Math.floor(config.auth.session.maxAge / 1000)
    );
    return sessionId;
  }

  /**
   * 删除已过期session
   * @param sessionId sessionId
   */
  static async deleteSession(sessionId: string): Promise<void> {
    await redis.del(`${SESSION_PREFIX}${sessionId}`);
  }

  /**
   * 签发token
   * @param userInfo 用户信息 注意：这里的 userInfo 必须包含 id 、type\ user_name 三个属性
   * @returns token
   */
  static createToken(userInfo: LoginUserInfo): string {
    return jwt.sign(userInfo, config.app.jwtSecret, { expiresIn: '1h' });
  }

  /**
   * 验证token
   * @param token token
   * @returns 用户信息
   */
  static verifyToken(token: string): LoginUserInfo | null {
    try {
      return jwt.verify(token, config.app.jwtSecret) as LoginUserInfo;
    } catch (error) {
      logger.error('验证 token 失败', { error });
      return null;
    }
  }

  /**
   * 登录成功
   * @param login_type 登录类型
   * @param user_type 用户类型
   * @param user_name 用户名
   * @param ip ip地址
   */
  static async loginSuccess(
    login_type: LoginType,
    user_type: LoginUserType,
    user_name: string,
    ip: string
  ): Promise<void> {
    if (config.app.saveLogs) {
      logger.info(`登录成功，用户：${user_name}，IP：${ip}`);
    }

    await this.resetAttempts(LOGIN_ATTEMPT_PREFIX, user_name);
    await this.resetAttempts(LOGIN_IP_PREFIX, ip);

    //TODO 登录成功后，记录登录日志
  }

  /**
   * 登录失败
   * @param login_type 登录类型
   * @param user_type 用户类型
   * @param user_name 用户名
   * @param ip ip地址
   */
  static async loginFail(
    login_type: LoginType,
    user_type: LoginUserType,
    user_name: string,
    ip: string
  ): Promise<void> {
    if (config.app.saveLogs) {
      logger.warn(`登录失败，用户：${user_name}，IP：${ip}`);
    }
    await this.recordAttempt(LOGIN_ATTEMPT_PREFIX, user_name);
    await this.recordAttempt(LOGIN_IP_PREFIX, ip);
    //TODO 登录失败后，记录登录日志
  }
}
